/**
 ******************************************************************************
 * @file	epcsx.c
 * @brief	epcsx 存储器通信

驱动层采用 spidev 控制器驱动依赖 spi_mxs，该驱动忽略 cs_change，
每一个spi_ioc_transfer里的所有内容均不在似 cs 信号翻转，翻转只存在于末尾

bug
    连续write、read数小时驱动存在错误，大概374行
TODO:introduce
 *
 @section Platform
    -#
 @section Library
    -#
 *
 * @section Depend
 *	-# spi_mxs.ko
 *	-# spidev.ko

- 2016-10-18,MenglongWu,MenglongWoo@aliyun.com
*/
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>

#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>


#include "proginfo.h"
// #include "ad5686-lib.h"
#include "ad5686.h"

#define ARRAY_SIZE(a)           (sizeof(a) / sizeof((a)[0]))

// EPCS命令

#define EPCS_WRITE_ENABLE       0x06
#define EPCS_WRITE_DISABLE      0x04

#define EPCS_READ_STATUS        0x05
#define EPCS_READ_BYTE          0x03
#define EPCS_READ_SILICON_ID    0xab  //读取silicon ID
#define EPCS_READ_SILICON_ID128 0x9f  //读取silicon ID

#define EPCS_WRITE_STATUE       0x01
#define EPCS_WRITE_BYTE         0x02

#define EPCS_ERASE_BULK         0xc7  //擦除所有扇区，大约需要5-10s
#define EPCS_ERASE_SECTOR       0xd8  //擦除指定扇区

// ==========================================================
static const uint32_t ad5686_channel_addr[] = {
    [AD5686_CH_0] = 1,
    [AD5686_CH_1] = 2,
    [AD5686_CH_2] = 4,
    [AD5686_CH_3] = 8,
    [4]           = 0x03,
    [5]           = 0x0f,
};

static const struct ad5686_chip_info chip_info[] = {
    [ID_AD5686] = {
	    .resolution    = 16,
	    .register_map  = AD5686_REG_MAP,
	    .communication = SPI,
	    .channel_addr  = ad5686_channel_addr,
    },
};
// ==========================================================


static void         epcs_write_enable(struct ad5686_dev *dev);
static unsigned int epcs_read_status(struct ad5686_dev *dev);
static int          match(struct ad5686_dev *dev, char pid);
static unsigned int epcs_read_silicon_id(struct ad5686_dev *dev);

// #define DEF_SPI "/dev/spidev1.1"
// static const char *device = DEF_SPI;
// static uint8_t mode;
// static uint8_t bits = 8;
// static uint32_t speed = 500000;
// static uint16_t delay;


struct epcsx_table
{
    char          pid;
    char	 *desc;
    unsigned long num_sector;
    unsigned long page_per_sector;
    unsigned long byte_per_page;
    unsigned long sector_sz;
};

// 识别epcsx设备类型匹配参数
static const struct epcsx_table table[] = {

    {
	    .pid             = 0x13,  // GD25Q80C
	    .desc            = "8Mbit GD25Q80C",
	    .num_sector      = 256,
	    .page_per_sector = 16,       // 每页有256字节，这个pdf没有体现，pdf只说一个sector有4KB、一个page有256B
	    .byte_per_page   = 256,      // 每页有256字节，这个pdf没有体现，pdf只说一个sector有4KB、一个page有256B
	    .sector_sz       = 0x01000,  // 64K
                                         // block = 一个block里有16个sector
    },

    {
	    .pid             = 0x10,  // EPCS1 1Mbit
	    .desc            = "1Mbit EPCS1",
	    .num_sector      = 4,
	    .page_per_sector = 128,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x08000,  // 32KB
    },
    {
	    .pid             = 0x1f4,  // EPCS1 1Mbit
	    .desc            = "1Mbit EPCS16",
	    .num_sector      = 8,  // 4,
	    .page_per_sector = 128,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x08000,  // 32KB
    },

    {
	    .pid             = 0x12,  // EPCS4 4Mbit
	    .desc            = "4Mbit EPCS4",
	    .num_sector      = 8,
	    .page_per_sector = 256,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x10000,  // 64KB
    },
    {
	    .pid             = 0x14,  // EPCS16
	    .desc            = "32Mbit EPCS16",
	    .num_sector      = 32,
	    .page_per_sector = 256,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x10000,  // 64KB
    },
    {
	    .pid             = 0x16,  // EPCS64
	    .desc            = "64Mbit EPCS64",
	    .num_sector      = 128,
	    .page_per_sector = 256,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x10000,  // 64KB
    },
    {
	    .pid             = 0x18,  // EPCS128
	    .desc            = "128Mbit EPCS128",
	    .num_sector      = 64,
	    .page_per_sector = 1024,
	    .byte_per_page   = 0x100,    // 256B
	    .sector_sz       = 0x40000,  // 256KB
    },
};


int ad5686_close(struct ad5686_dev *dev)
{
    assert(dev != NULL);

    close(dev->fd);
    // free(dev);
    dev = NULL;
    return 0;
}
char *IO_CLK;
char *IO_SS;
char *IO_MOSI;
char *IO_MISO;
void  init_gpio()
{
    IO_CLK  = getenv("IO_CLK");
    IO_SS   = getenv("IO_SS");
    IO_MOSI = getenv("IO_MOSI");
    IO_MISO = getenv("IO_MISO");
}

static const char *dev_desc = "name-ad5686";

int ad5686_open(char *device, struct ad5686_dev **dev, unsigned long ispeed)
{
    init_gpio();
    // todo spinlock
    // unsigned char mode = SPI_MODE_3 | SPI_CS_HIGH;
    unsigned char mode  = SPI_MODE_0;
    unsigned char bits  = 8;
    unsigned long speed = 12 * 1000 * 1000;  // 12MHz
    // unsigned short delay;
    int fd;

    if (ispeed) {
	speed = ispeed;
    }

    fd = open(device, O_RDWR);
    if (fd == 0) {
	return -1;
    }


    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    struct ad5686_dev *pdev;
    pdev = (struct ad5686_dev *)malloc(sizeof(struct ad5686_dev));
    if (pdev == NULL) {
	goto _fail1;
    }
    bzero(pdev, sizeof(struct ad5686_dev));
    // todo spinlock
    pdev->fd         = fd;
    pdev->act_device = ID_AD5686;
    pdev->desc       = dev_desc;
    // ad5686_write_update_register(pdev, 0, 0xffff);
    // ad5686_update_register(pdev, 0);

    // ad5686_write_update_register(pdev, 1, 0xffff/2);
    // ad5686_update_register(pdev, 1);

    // ad5686_write_update_register(pdev, 2, 0xffff/3);
    // ad5686_update_register(pdev, 2);

    // ad5686_write_update_register(pdev, 3, 0xffff/4);
    // ad5686_update_register(pdev, 3);
    // int kk ;
    // kk = ad5686_read_back_register(pdev, 3);
    // printf("kk %x\r\n", kk);
    // exit(1);
    // ad5686_write_update_register(pdev, 0, 0x11);
    // ad5686_write_update_register(pdev, -2, 0x11);

    // if (-1 == epcs_read_silicon_id(pdev)) {
    // 	goto _fail2;
    // }
    *dev = pdev;
    return 0;
_fail1:
    close(fd);
    return -1;
_fail2:
    ad5686_close(pdev);
    return -2;
}


/**
 * @brief	写使能
 * @param[in]	dev 设备描述符
 * @remarks 任何与写入相关的操作在写入前必须打开写入使能
 * @see	epcs_erase_bulk
 * @see	epcs_erase_sector
 * @see	epcs_write_page
 */

static void epcs_write_enable(struct ad5686_dev *dev)
{
    // uint8_t tx[] = {EPCS_WRITE_ENABLE};
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 	},
    // };
    // // todo spinlock
    // ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);
    // // todo spinlock
}

/**
 * @brief	获取设备状态
 * @param[in]	dev 设备描述
 * @retval	-1 总线错误
         0：表示处于空闲模式\n
         3: 表示芯片正在执行写入、擦除状态，不能接受除EPCS_READ_STATUS意外的指令
 * @remarks
 */

static unsigned int epcs_read_status(struct ad5686_dev *dev)
{
    // uint8_t tx[] = {EPCS_READ_STATUS};
    // uint8_t rx[1];
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 		.cs_change = 1,
    // 	},
    // 	{
    // 		.tx_buf = (unsigned long)NULL,
    // 		.rx_buf = (unsigned long)rx,
    // 		.len = ARRAY_SIZE(rx),
    // 		.cs_change = 0,
    // 	},
    // };
    // int ret;

    // ret = ioctl(dev->fd, SPI_IOC_MESSAGE(2), &arr);
    // if (ret == -1) {
    // 	return -1;
    // }
    // return rx[0];
}

/**
 * @brief	查询是否设备能匹配epcsx，匹配则填充设备描述符
 * @param[in]	dev 设备描述
 * @param[in]	pid 扫描到的设备ID
 * @retval	0 匹配
 * @retval	-1 不匹配
 */

static int match(struct ad5686_dev *dev, char pid)
{
    // int i;
    // printf("pid = %x----\r\n", pid);
    // for ( i = 0; i < ARRAY_SIZE(table); i++) {
    // 	if (table[i].pid == pid) {
    // 		break;
    // 	}
    // }
    // if (ARRAY_SIZE(table) != i) {
    // 	dev->pid             = table[i].pid;
    // 	dev->desc             = table[i].desc;
    // 	dev->num_sector      = table[i].num_sector;
    // 	dev->page_per_sector = table[i].page_per_sector;
    // 	dev->byte_per_page   = table[i].byte_per_page;
    // 	dev->sector_sz       = table[i].sector_sz;
    // 	return 0;
    // } else {
    // 	dev->desc = "unknow";
    // }
    // return -1;
}

/**
 * @brief	探测设备ID
 * @param[in]	dev 设备描述
 * @retval	0 找到
 * @retval	-1 没找到
 */

static unsigned int epcs_read_silicon_id(struct ad5686_dev *dev)
{
    uint8_t                 tx[] = { EPCS_READ_SILICON_ID, 0x12, 0x13 };
    uint8_t                 rx[3];
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf    = (unsigned long)tx,
		.rx_buf    = (unsigned long)NULL,
		.len       = ARRAY_SIZE(tx),
		.cs_change = 1,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)rx,
		.len       = ARRAY_SIZE(rx),
		.cs_change = 0,
	},
    };

    ioctl(dev->fd, SPI_IOC_MESSAGE(2), &arr);
    // ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);
    if (0 == match(dev, rx[0])) {
	return 0;
    }

    return -1;
}

int epcs_erase_bulk(struct ad5686_dev *dev)
{
    // return 0;
    // epcs_write_enable(dev);
    // uint8_t tx[] = {EPCS_ERASE_BULK};
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 	},
    // };
    // // todo spinlock
    // ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);

    // int count = 20;
    // do {
    // 	sleep(1);
    // } while(epcs_read_status(dev) != 0 && --count);
    // if (count == 0) {
    // 	return -1;
    // } else {
    // 	return 0;
    // }
}


int epcs_erase_sector(struct ad5686_dev *dev, unsigned int addr)
{
    // return 0;
    // epcs_write_enable(dev);
    // uint8_t tx[] = {EPCS_ERASE_SECTOR,
    //                 (uint8_t)(addr >> 16),
    //                 (uint8_t)(addr >> 8),
    //                 (uint8_t)addr
    //                };
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 	},
    // };
    // // todo spinlock
    // ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);

    // int count = 100;
    // while(epcs_read_status(dev) != 0 && --count) {
    // 	usleep(20000);
    // }
    // if (count == 0) {
    // 	return -1;
    // } else {
    return 0;
    // }
}


int epcs_write_page(struct ad5686_dev *dev, unsigned int addr, unsigned char *data, unsigned int len)
{
    // epcs_write_enable(dev);

    // uint8_t tx[] = {EPCS_WRITE_BYTE,
    //                 (uint8_t)(addr >> 16),
    //                 (uint8_t)(addr >> 8),
    //                 (uint8_t)addr
    //                };
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 	},
    // 	{
    // 		.tx_buf = (unsigned long)data,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = len,
    // 	},
    // };
    // ioctl(dev->fd, SPI_IOC_MESSAGE(2), &arr);

    // int count = 1000000;
    // while(epcs_read_status(dev) != 0 && --count);
    // if (count == 0) {
    // 	return -1;
    // }
    return 0;
}


int epcs_read_page(struct ad5686_dev *dev, unsigned int addr, unsigned char *data, unsigned int len)
{
    // uint8_t tx[] = {EPCS_READ_BYTE,
    //                 (uint8_t)(addr >> 16),
    //                 (uint8_t)(addr >> 8),
    //                 (uint8_t)addr
    //                };
    // struct spi_ioc_transfer arr[] =  {
    // 	{
    // 		.tx_buf = (unsigned long)tx,
    // 		.rx_buf = (unsigned long)NULL,
    // 		.len = ARRAY_SIZE(tx),
    // 		.cs_change = 1,
    // 	},
    // 	{
    // 		.tx_buf = (unsigned long)NULL,
    // 		.rx_buf = (unsigned long)data,
    // 		.len = len ,
    // 		.cs_change = 0,
    // 	},
    // };
    // int count = 1000000;
    // while(epcs_read_status(dev) != 0 && --count);
    // if (count == 0) {
    // 	return -1;
    // }
    // return ioctl(dev->fd, SPI_IOC_MESSAGE(2), &arr);
}

unsigned int epcs_size(struct ad5686_dev *dev)
{
    // return dev->byte_per_page * dev->page_per_sector * dev->num_sector;
}


void delay_us(int a)
{
}
char spi_cmd[256];
void spi_sck_set(int dev_spi, int val)
{
    snprintf(spi_cmd, 256, "echo %d > /sys/class/gpio/gpio%s/value", val, IO_CLK);
    system(spi_cmd);
}
void ad5686_cs_set(int val)
{
    snprintf(spi_cmd, 256, "echo %d > /sys/class/gpio/gpio%s/value", val, IO_SS);
    system(spi_cmd);
}
void spi_mosi_set(int dev_spi, int val)
{
    snprintf(spi_cmd, 256, "echo %d > /sys/class/gpio/gpio%s/value", val, IO_MOSI);
    system(spi_cmd);
}

void ad5686_write(uint8_t ch, uint16_t argv)
{
    int      ix;
    uint32_t data;

    data = ((0x30 + ch) << 16) + argv;

    ad5686_cs_set(0);
    delay_us(1);

    for (ix = 0; ix < 24; ix++)
    {
	spi_sck_set(NULL, 1);
	if ((data & 0x800000) == 0x800000)
	{
	    spi_mosi_set(NULL, 1);
	}
	else
	{
	    spi_mosi_set(NULL, 0);
	}

	data <<= 1;
	delay_us(1);
	spi_sck_set(NULL, 0);
	delay_us(1);
    }

    delay_us(1);
    ad5686_cs_set(1);
}

void ad5686_write_3(char *tx)
{
    int      ix;
    uint32_t data;

    data = (tx[0] << 16) + (tx[1] << 8) + (tx[2]);

    ad5686_cs_set(0);
    delay_us(1);

    for (ix = 0; ix < 24; ix++)
    {
	spi_sck_set(NULL, 1);
	if ((data & 0x800000) == 0x800000)
	{
	    spi_mosi_set(NULL, 1);
	}
	else
	{
	    spi_mosi_set(NULL, 0);
	}

	data <<= 1;
	delay_us(1);
	spi_sck_set(NULL, 0);
	delay_us(1);
    }

    delay_us(1);
    ad5686_cs_set(1);
}
#if 0
unsigned int spi_write_and_read(
	// struct ad5686_dev *dev, 
	struct ad5686_dev *dev, 
	char *tbuf, 
	int tlen, 
	char *rbuf, 
	int rlen)
{
	// ad5686_write(0, 60000);
	// uint8_t tx[] = {EPCS_READ_SILICON_ID, 0x12, 0x13};
	// uint8_t rx[3];
	struct spi_ioc_transfer arr[] =  {
        {
                .tx_buf      = (unsigned long)tbuf,
                .rx_buf      = (unsigned long)NULL,
                .len         = tlen,
                .cs_change   = 0,
                .delay_usecs = 0,
        },
        {
                .tx_buf    = (unsigned long)NULL,
                .rx_buf    = (unsigned long)rbuf,
                .len       = rlen,
                .cs_change = 0,
        },
	};

	ioctl(dev->fd, SPI_IOC_MESSAGE(2), &arr);
	// ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);

	return -1;
}
#else
unsigned int spi_write_and_read(
	// struct ad5686_dev *dev,
	struct ad5686_dev *dev,
	char              *tbuf,
	int                tlen,
	char              *rbuf,
	int                rlen)
{
    // ad5686_write_3(tbuf);
    // return -1;
    // ad5686_write(0, 60000);
    // uint8_t tx[] = {EPCS_READ_SILICON_ID, 0x12, 0x13};
    // uint8_t rx[3];
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf      = (unsigned long)tbuf,
		.rx_buf      = (unsigned long)NULL,
		.len         = tlen,
		.cs_change   = 0,
		.delay_usecs = 0,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)rbuf,
		.len       = rlen,
		.cs_change = 0,
	},
    };
    ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);
    // ioctl(dev->fd, SPI_IOC_MESSAGE(1), &arr);

    return -1;
}

#endif

/**************************************************************************/ /**
                                                                              * @brief Write to input shift register.
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param command - Command control bits.
                                                                              * @param address - The address bits.
                                                                              * @param data    - Data to be written in input register.
                                                                              *
                                                                              * @return  read_back_data - value read from register.
                                                                              ******************************************************************************/
uint16_t ad5686_set_shift_reg(struct ad5686_dev *dev,
                              uint8_t            command,
                              uint8_t            address,
                              uint16_t           data)
{
    uint8_t  data_buff[PKT_LENGTH] = { 0, 0, 0 };
    uint16_t read_back_data        = 0;

    if (chip_info[ID_AD5686].register_map == AD5686_REG_MAP) {
	data_buff[0] = ((command & AD5686_CMD_MASK) << CMD_OFFSET) |
	               (address & ADDR_MASK);
	data_buff[1] = (data & AD5686_MSB_MASK) >> AD5686_MSB_OFFSET;
	data_buff[2] = (data & AD5686_LSB_MASK);
    }
    else {
	data_buff[0] = ((command & AD5683_CMD_MASK) << CMD_OFFSET) |
	               ((data >> AD5683_MSB_OFFSET) & AD5683_MSB_MASK);
	data_buff[1] = (data >> AD5683_MIDB_OFFSET) & AD5683_MIDB_MASK;
	data_buff[2] = (data & AD5683_LSB_MASK) << AD5683_LSB_OFFSET;
    }

    if (chip_info[ID_AD5686].communication == SPI) {
	spi_write_and_read(dev, data_buff, PKT_LENGTH, data_buff, PKT_LENGTH);
	if (chip_info[ID_AD5686].register_map == AD5686_REG_MAP) {
	    read_back_data = (data_buff[1] << AD5686_MSB_OFFSET) | data_buff[2];
	}
	else {
	    read_back_data = (data_buff[0] & AD5683_CMD_MASK) << AD5683_MSB_OFFSET |
	                     data_buff[1] << AD5683_MIDB_OFFSET |
	                     data_buff[2] >> AD5683_LSB_OFFSET;
	}
    }
    else {
	// no_os_i2c_write(dev->i2c_desc, data_buff, PKT_LENGTH, 1);
    }

    return read_back_data;
}


/**************************************************************************/ /**
                                                                              * @brief Write to Input Register n (dependent on LDAC)
                                                                              *
                                                                              * @param dev      - The device structure.
                                                                              * @param channel  - The chosen channel to write to.
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @param data - desired value to be written in register.
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_write_register(struct ad5686_dev       *dev,
                           enum ad5686_dac_channels channel,
                           uint16_t                 data)
{
    uint8_t data_offset = MAX_RESOLUTION -
                          chip_info[ID_AD5686].resolution;
    uint8_t address = chip_info[ID_AD5686].channel_addr[channel];

    ad5686_set_shift_reg(dev, AD5686_CTRL_WRITE, address, data << data_offset);
}

/**************************************************************************/ /**
                                                                              * @brief Update DAC Register n with contents of Input Register n
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param channel  - The chosen channel to write to.
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_update_register(struct ad5686_dev       *dev,
                            enum ad5686_dac_channels channel)
{
    assert(channel < (ARRAY_SIZE(ad5686_channel_addr)));
    assert(channel >= 0);
    uint8_t address = chip_info[ID_AD5686].channel_addr[channel];

    ad5686_set_shift_reg(dev, AD5686_CTRL_UPDATE, address, 0);
}


/**************************************************************************/ /**
                                                                              * @brief Write to and update DAC channel n
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param channel  - The chosen channel to write to.
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @param data    - Desired value to be written in register.
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_write_update_register(struct ad5686_dev       *dev,
                                  enum ad5686_dac_channels channel,
                                  uint16_t                 data)
{
    assert(channel < (ARRAY_SIZE(ad5686_channel_addr)));
    assert(channel >= 0);
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    uint8_t data_offset = MAX_RESOLUTION -
                          chip_info[dev->act_device].resolution;
    uint8_t address = chip_info[dev->act_device].channel_addr[channel];

    // printf("off %d addr %d\r\n", data_offset, address);
    ad5686_set_shift_reg(dev, AD5686_CTRL_WRITEUPDATE, address, data << data_offset);
}

/**************************************************************************/ /**
                                                                              * @brief Read back Input Register n
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param channel - The channel which will be read back. Note: only one
                                                                              *                  channel should be selected, if there will be selected
                                                                              *                  more than one channel, the channel A will be read back
                                                                              *                  by default
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @return read_back_data - value read from register.
                                                                              ******************************************************************************/
uint16_t ad5686_read_back_register(struct ad5686_dev       *dev,
                                   enum ad5686_dac_channels channel)
{

    uint16_t read_back_data = 0;
    uint16_t offset         = MAX_RESOLUTION -
                      chip_info[ID_AD5686].resolution;
    uint8_t address        = chip_info[ID_AD5686].channel_addr[channel];
    uint8_t rb_data_i2c[3] = { 0 };

    if (chip_info[ID_AD5686].communication == SPI) {
	ad5686_set_shift_reg(dev, AD5686_CTRL_RB_REG, address, 0);
	read_back_data = ad5686_set_shift_reg(dev, AD5686_CTRL_NOP, 0, 0);
	read_back_data >>= offset;
    }
    else {
	if (chip_info[ID_AD5686].register_map == AD5683_REG_MAP)
	    rb_data_i2c[0] = (AD5683_CTRL_RB_REG << CMD_OFFSET) |
	                     address;
	else
	    rb_data_i2c[0] = (AD5686_CTRL_RB_REG << CMD_OFFSET) |
	                     address;

	// no_os_i2c_write(dev->i2c_desc, rb_data_i2c, 3, 0);
	// no_os_i2c_read(dev->i2c_desc, rb_data_i2c, 2, 1);
	read_back_data = (rb_data_i2c[0] << 8) | rb_data_i2c[1];
    }

    return read_back_data;
}


/**************************************************************************/ /**
                                                                              * @brief Set Power-down mode for DAC channel n
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param channel  - The chosen channel to change the power-down mode.
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @param mode    - Power-down operation modes.
                                                                              *                  Accepted values:
                                                                              *                  'AD5686_PWRM_NORMAL' - Normal Mode
                                                                              *                  'AD5686_PWRM_1K' - Power-down mode 1kOhm to GND
                                                                              *                  'AD5686_PWRM_100K' - Power-down mode 100kOhm to GND
                                                                              *                  'AD5686_PWRM_THREESTATE' - Three-State
                                                                              *                  'AD5686_PWRM_100K' is not available for AD5674R/AD5679R.
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_power_mode(struct ad5686_dev       *dev,
                       enum ad5686_dac_channels channel,
                       uint8_t                  mode)
{
    uint8_t address = chip_info[ID_AD5686].channel_addr[channel];

    if (chip_info[ID_AD5686].register_map == AD5686_REG_MAP) {
	/* AD5674R/AD5679R have 16 channels and 2 powerdown registers */
	if (channel > AD5686_CH_7)
	    channel -= AD5686_CH_7 + 1;
	dev->power_down_mask &= ~(0x3 << (channel * 2));
	dev->power_down_mask |= (mode << (channel * 2));
	ad5686_set_shift_reg(dev, AD5686_CTRL_PWR, address, dev->power_down_mask);
    }
    else {
	ad5686_set_shift_reg(dev, AD5683_CMD_WR_CTRL_REG, address, AD5683_CTRL_PWRM(mode));
    }
}


/**************************************************************************/ /**
                                                                              * @brief Set hardware LDAC mask register
                                                                              *
                                                                              * @param dev     - The device structure.
                                                                              * @param channel - In case of which channel ignore transitions on the LDAC
                                                                              *                    pin.
                                                                              *                    Accepted values:	AD5686_CH_0
                                                                              *					AD5686_CH_1
                                                                              *					AD5686_CH_2
                                                                              *					AD5686_CH_3
                                                                              *					AD5686_CH_4
                                                                              * 					AD5686_CH_5
                                                                              *					AD5686_CH_6
                                                                              * 					AD5686_CH_7
                                                                              *					AD5686_CH_8
                                                                              *                              	AD5686_CH_9
                                                                              *					AD5686_CH_10
                                                                              *					AD5686_CH_11
                                                                              *					AD5686_CH_12
                                                                              *					AD5686_CH_13
                                                                              *					AD5686_CH_14
                                                                              *					AD5686_CH_15
                                                                              * @param enable - Enable/disable channel.
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_ldac_mask(struct ad5686_dev       *dev,
                      enum ad5686_dac_channels channel,
                      uint8_t                  enable)
{
    if (chip_info[dev->act_device].register_map == AD5686_REG_MAP) {
	dev->ldac_mask &= ~(0x1 << channel);
	dev->ldac_mask |= (enable << channel);
	ad5686_set_shift_reg(dev, AD5686_CTRL_LDAC_MASK, 0, dev->ldac_mask);
    }
}


/**************************************************************************/ /**
                                                                              * @brief Software reset (power-on reset)
                                                                              *
                                                                              * @param dev - The device structure.
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_software_reset(struct ad5686_dev *dev)
{
    if (chip_info[dev->act_device].register_map == AD5686_REG_MAP)
	ad5686_set_shift_reg(dev, AD5686_CTRL_SWRESET, 0, 0);
    else
	ad5686_set_shift_reg(dev, AD5683_CMD_WR_CTRL_REG, 0, AD5683_SW_RESET);
}


/**************************************************************************/ /**
                                                                              * @brief Write to Internal reference setup register
                                                                              *
                                                                              * @param dev   - The device structure.
                                                                              * @param value - The internal reference register value
                                                                              *                Example : 'AD5686_INTREF_EN' - enable internal reference
                                                                              *                            'AD5686_INTREF_DIS' - disable internal reference
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_internal_reference(struct ad5686_dev *dev,
                               uint8_t            value)
{
    if (chip_info[dev->act_device].register_map == AD5686_REG_MAP)
	ad5686_set_shift_reg(dev, AD5686_CTRL_IREF_REG, 0, value);
    else
	ad5686_set_shift_reg(dev, AD5683_CMD_WR_CTRL_REG, 0, AD5683_CTRL_INT_REF(value));
}


/**************************************************************************/ /**
                                                                              * @brief Set up DCEN register (daisy-chain enable)
                                                                              *
                                                                              * @param dev   - The device structure.
                                                                              * @param value - Enable or disable daisy-chain mode
                                                                              *                Example : 'AD5686_DC_EN' - daisy-chain enable
                                                                              *                          'AD5686_DC_DIS' - daisy-chain disable
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_daisy_chain_en(struct ad5686_dev *dev,
                           uint8_t            value)
{
    if (chip_info[dev->act_device].register_map == AD5686_REG_MAP)
	ad5686_set_shift_reg(dev, AD5686_CTRL_DCEN, 0, value);
    else
	ad5686_set_shift_reg(dev, AD5683_CMD_WR_CTRL_REG, 0, AD5683_CTRL_DCEN(value));
}

/**************************************************************************/ /**
                                                                              * @brief Set up readback register (readback enable)
                                                                              *
                                                                              * @param dev   - The device structure.
                                                                              * @param value - Enable or disable daisy-chain mode
                                                                              *                Example : 'AD5686_RB_EN' - daisy-chain enable
                                                                              *                          'AD5686_RB_DIS' - daisy-chain disable
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
void ad5686_read_back_en(struct ad5686_dev *dev,
                         uint8_t            value)
{
    if (chip_info[dev->act_device].register_map == AD5686_REG_MAP)
	ad5686_set_shift_reg(dev, AD5686_CTRL_RB_REG, 0, value);
}

/**************************************************************************/ /**
                                                                              * @brief Set Gain mode
                                                                              *
                                                                              * @param dev   - The device structure.
                                                                              * @param value - Gain modes.
                                                                              *                Accepted values:
                                                                              *                Example : 'AD5683_GB_VREF' - 0V to VREF
                                                                              *                          'AD5683_GB_2VREF' - 0V to 2xVREF
                                                                              *
                                                                              * @return None.
                                                                              ******************************************************************************/
int32_t ad5686_gain_mode(struct ad5686_dev *dev, uint8_t value)
{
    if (chip_info[dev->act_device].register_map == AD5683_REG_MAP)
	return ad5686_set_shift_reg(dev, AD5683_CMD_WR_CTRL_REG, 0, AD5683_CTRL_GM(value));
    return -1;
}


#ifdef PRJ_NAME
PROG_INFO(project, PRJ_NAME);
#endif

#if defined(PRJ_VERSION) && defined(PRJ_PATCHLEVEL) && defined(PRJ_SUBLEVEL)
PROG_INFO(version, PRJ_VERSION "." PRJ_PATCHLEVEL "." PRJ_SUBLEVEL);
#endif

#ifdef BUILD_DATE
PROG_INFO(build, BUILD_DATE);
#endif

PROG_INFO(Author, "MenglongWu");
PROG_INFO(depend, "spi_mxs.ko spidev.ko");
PROG_INFO(description, "The serial configuration devices EPCSx interface");
